package rs.controller

import akka.actor.typed.receptionist.{Receptionist, ServiceKey}
import akka.actor.typed.{ActorRef, Behavior}
import akka.actor.typed.scaladsl.{AbstractBehavior, ActorContext, Behaviors, TimerScheduler}
import akka.cluster.client.ClusterClient
import org.bytedeco.javacv.Frame
import org.bytedeco.opencv.opencv_core.Mat
import rs.actor.*
import rs.detector.*

import java.util.concurrent.TimeUnit
import scala.concurrent.duration.FiniteDuration

object MainController {

  /**
   * for find remote controller actor in Pi
   * should use same id in RaspPi/rs.controllerKey in CarController
   */
  val controllerKey = ServiceKey[BaseCommand]("PiCtl")
  var controllerList = Set[ActorRef[BaseCommand]]()

  def apply(): Behavior[String]= {
    Behaviors.setup[String] { ctx =>

      val blist = ctx.spawn(Behaviors.receiveMessage{ (list: Receptionist.Listing) =>
        this.controllerList = list.allServiceInstances(controllerKey)
        println(s"update controller list: ${this.controllerList}")
        Behaviors.same
      }, "ctlList")
      ClusterClient
      ctx.system.receptionist ! Receptionist.Subscribe(controllerKey, blist.ref)

      new MainController(ctx)
    }
  }
}

class MainController(ctx: ActorContext[String]) extends AbstractBehavior[String](ctx) {

  import  MainController.controllerList

//  val vshow: ActorRef[Frame] = ctx.spawn(VideoShow().show(), "vshow")
//  val faceDect: ActorRef[Mat] = ctx.spawn(FaceDnnActor(vshow).detect(), "detector")
//  val grabber = ctx.spawn(VideoGrabber(faceDect).grab(), "grabber")

  val joy: ActorRef[ActorRef[JoyCommand]] = ctx.spawn(JoySticker(), "joy")

  val webActor: ActorRef[BaseCommand] = ctx.spawn(WebClientActor(), "webClient")

  var tripodP = 90
  var tripodD = 90
  webActor.tell(TripodInfo(tripodP, tripodD))

  /**
   * send command to Pi
   * @param baseCommand
   */
  def send(baseCommand: BaseCommand): Unit = {
    if (controllerList.size == 0) {
      println("no ctl found.")
    } else {
      controllerList.foreach( _.tell(baseCommand))
    }
  }

  val joyEventHandler = ctx.spawn(Behaviors.receive[JoyCommand]{ (ctx, ev:JoyCommand) =>
    ev match {
      case JoyBtnEvent(btnNum, isDown) => {
        println(s"get btn ${btnNum}")
        val tripodBaseDegree  =3
        val tripodUpdateDelay = 20
        btnNum match {
          case 3 => webActor ! (if isDown then TripodVelocity(2, 0) else TripodVelocity(0, 0))
          case 1 => webActor ! (if isDown then TripodVelocity(-2, 0) else TripodVelocity(0, 0))
          case 0 => webActor ! (if isDown then TripodVelocity(0, -2) else TripodVelocity(0, 0))
          case 2 => webActor ! (if isDown then TripodVelocity(0, 2) else TripodVelocity(0, 0))
          case 4 => webActor ! (if isDown then MoveRight(70) else Stop())
          case 5 => webActor ! (if isDown then MoveLeft(70) else Stop())
          case 9 => webActor ! LedToggle()
          case 8 => webActor ! LedToggle()
        }
      }
      case JoyAxisEvent(axisNum, axisValue) => {
        println(s"get btn ${axisNum} ${axisValue}")
        send(axisNum match {
          case 1 => {
            axisValue match {
              case x if x==0 => Stop()
              case x if x>0 => Backward(50)
              case x if x<0 => Forward(50)
            }
          }
          case 0 => {
            axisValue match {
              case x if x==0 => Stop()
              case x if x>0 => TurnRight(50)
              case x if x<0 => TurnLeft(50)
            }
          }
          case _ => Stop()
        })
      }
    }
    Behaviors.same
  }, "joyHandler")

  override def onMessage(msg: String): Behavior[String] = {
    ctx.log.info(s"$msg monitor")
    //grabber.tell("http://192.168.31.242:8081")
    joy.tell(joyEventHandler)
    ctx.spawn( Behaviors.setup{ (ctx2: ActorContext[String]) =>
      Behaviors.withTimers[String] { (timer: TimerScheduler[String]) =>
        timer.startTimerWithFixedDelay("asdf", FiniteDuration(1, TimeUnit.SECONDS))
        Behaviors.receiveMessage[String] { msg =>
          println(s"controllers: ${controllerList}")
          controllerList.foreach { ctl =>
            ctl.tell(rs.actor.Forward(1.0))
          }
          Behaviors.same
        }
      }
    }, "asd")
    Behaviors.empty
  }
}
